package com.yonyou.pmclouds.plan.rmiimpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.billstatus.BillStatusConst;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoInsertMultiProcessor;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoInsertProcessor;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoUpdateProcessor;
import com.yonyou.pmclouds.basecom.bp.template.MultiInsertBpTemplate;
import com.yonyou.pmclouds.basecom.bp.template.SingleDeleteBpTemplate;
import com.yonyou.pmclouds.basecom.bp.template.SingleInsertBpTemplate;
import com.yonyou.pmclouds.basecom.bp.template.SingleUpdateBpTemplate;
import com.yonyou.pmclouds.basecom.consts.BillTypeConst;
import com.yonyou.pmclouds.basecom.entity.SuperVO;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.exception.ConcurrentOperationException;
import com.yonyou.pmclouds.basecom.exception.DataModifiedException;
import com.yonyou.pmclouds.basecom.exception.IllegalRequestArgException;
import com.yonyou.pmclouds.basecom.lock.AutoReleaseLockUtil;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.basecom.util.DateUtils;
import com.yonyou.pmclouds.externalservice.entity.FailedExternalRecordVO;
import com.yonyou.pmclouds.externalservice.rmiitf.FailedExternalRecordMaintain;
import com.yonyou.pmclouds.externalservice.rmiitf.FailedExternalRecordQuery;
import com.yonyou.pmclouds.measureindex.entity.MeasureIndexHistoryVO;
import com.yonyou.pmclouds.measureindex.service.rmiitf.MeasureIndexHisMaintin;
import com.yonyou.pmclouds.organization.entity.OrganizationVO;
import com.yonyou.pmclouds.organization.rmiitf.OrganizationQuery;
import com.yonyou.pmclouds.picture.entity.PictureParamVO;
import com.yonyou.pmclouds.picture.rmiitf.PictureRemoteService;
import com.yonyou.pmclouds.plan.entity.*;
import com.yonyou.pmclouds.plan.mapper.*;
import com.yonyou.pmclouds.plan.plandriven.rmiitf.IPlanDriven;
import com.yonyou.pmclouds.plan.planindex.entity.PlanIndexVO;
import com.yonyou.pmclouds.plan.planindex.rmiitf.IPlanIndexMaintain;
import com.yonyou.pmclouds.plan.planindex.rmiitf.IPlanIndexQuery;
import com.yonyou.pmclouds.plan.processor.*;
import com.yonyou.pmclouds.plan.rmiitf.PlanWriteMaintain;
import com.yonyou.pmclouds.plan.rmiitf.ProjectPlanMaintain;
import com.yonyou.pmclouds.plan.rmiitf.ProjectPlanQuery;
import com.yonyou.pmclouds.projecttype.entity.ProjectTypeVO;
import com.yonyou.pmclouds.projecttype.rmiitf.ProjectTypeQuery;
import com.yonyou.pmclouds.workflow.aspect.PMApprove;
import com.yonyou.pmclouds.workflow.service.rmiitf.IFlowApproveBuziService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

@Slf4j
@Deprecated
@Service(version = ApplicationConsts.APPLICATION_VERSION, interfaceClass = ProjectPlanMaintain.class, timeout = 1200000)
public class ProjectPlanMaintainImpl implements ProjectPlanMaintain, IFlowApproveBuziService {
    private static final String LOCK_PREFIX = "plan_";

    private static final String[] UPDATE_FIELDS = new String[]{CommonFieldConst.SNAME, ProjectPlanVO.DESCRIPTION, ProjectPlanVO.DEPEND_PLAN, ProjectPlanVO.MANAGER};
    private static final String[] UPDATE_LEAF_FIELDS = new String[]{CommonFieldConst.SNAME, ProjectPlanVO.DESCRIPTION,
            ProjectPlanVO.DEPEND_PLAN, ProjectPlanVO.EXPE_DURATION, ProjectPlanVO.EXPE_END_DATE, ProjectPlanVO.EXPE_START_DATE, ProjectPlanVO.MANAGER,
            ProjectPlanVO.INDEX_NUMBER, ProjectPlanVO.PK_MILESTONE, ProjectPlanVO.RELATED_BILL_TYPE, ProjectPlanVO.COMPLETED_STATUS};
    private static final String[] UPDATE_ALL_FIELDS = new String[]{CommonFieldConst.SCODE, CommonFieldConst.SNAME, ProjectPlanVO.DEPEND_PLAN,
             ProjectPlanVO.EXPE_DURATION, ProjectPlanVO.EXPE_END_DATE, ProjectPlanVO.EXPE_START_DATE, ProjectPlanVO.MANAGER, ProjectPlanVO.CHILD_COUNT,
            ProjectPlanVO.SEQ, ProjectPlanVO.BILLSTATUS, ProjectPlanVO.LAST_FLAG, ProjectPlanVO.INDEX_NUMBER,
            ProjectPlanVO.PK_MILESTONE, ProjectPlanVO.RELATED_BILL_TYPE, ProjectPlanVO.COMPLETED_STATUS, ProjectPlanVO.PK_DEPARTMENT};
    private static final String[] UPDATE_STATUS_FIELD = new String[]{ProjectPlanVO.BILLSTATUS};
    private static final String[] UPDATE_LASTFLAG_FIELD = new String[]{ProjectPlanVO.LAST_FLAG};

    private static final int MAX_LEVEL = 20;
    private static final int SAVE_ACTION = 1;
    private static final int ADJUST_ACTION = 2;
    @Autowired
    private ProjectPlanMapper mapper;
    @Autowired
    private PictureRemoteService picService;
    @Autowired
    private PlanProgressMapper progMapper;
    @Autowired
    private PlanProgressHistoryMapper hisMapper;
    @Autowired
    private FailedExternalRecordMaintain recordMaintain;
    @Autowired
    private FailedExternalRecordQuery recordQuery;
    @Autowired
    private PlanWriteMaintain planWriteMaintain;
    @Autowired
    private PlanWriteMapper planWriteMapper;
    @Autowired
    private IPlanIndexMaintain planIndexMaintain;
    @Autowired
    private MeasureIndexHisMaintin measureIndexHisMaintin;
    @Autowired
    private IPlanIndexQuery planIndexQuery;
    @Autowired
    private IPlanDriven planDriven;
    @Autowired
    private OrganizationQuery organizationQuery;
    @Autowired
    private PlanTempletMapper planTempletMapper;
    @Autowired
    private ProjectTypeQuery projectTypeQuery;

    @Autowired
    private ProjectPlanQuery projectPlanQuery;

    @Autowired
    private ScheduleMapper scheduleMapper;

    @Autowired
    private ScheduleItemMapper scheduleItemMapper;

    @Override
    public String insertPlan(ProjectPlanVO plan) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + plan.getPkProject())) {
            throw new ConcurrentOperationException();
        }
        // 查询并校验父级的合法性
        String parentId = plan.getPkParent();
        ProjectPlanVO parent = null;
        if (!StringUtils.isEmpty(parentId)) {
            parent = mapper.getById(parentId);
            if (parent == null) {
                throw new IllegalRequestArgException();
            }
        } else {
            plan.setPkParent(null);
        }
        // 设置本节点的count
        // 调整父节点的count
        plan.setChildCount(0);
        if (parent != null) {
            parent.setChildCount(parent.getChildCount() + 1);
            mapper.updateFields(parent, new String[]{CommonFieldConst.CHILD_COUNT});
        }
        SingleInsertBpTemplate<ProjectPlanVO> bp = new SingleInsertBpTemplate<>(mapper);
        bp.addBeforeProcessor(new AuditInfoInsertProcessor<ProjectPlanVO>());
        // 生成编码
        bp.addBeforeProcessor(new PlanCodeGenProcessor(parent, mapper));
        // 校验层级
        bp.addBeforeProcessor(new PlanLevelCheck());
        // 设置root、parent
        bp.addBeforeProcessor(new PlanRootProcessor(parent));
        // 校验是否为根计划的创建人
        bp.addBeforeProcessor(new PlanInsertPermCheck(mapper));
        // 校验上级进度是否为0，否则不可增加下级
        bp.addBeforeProcessor(new PlanInsertProgressCheck(parent, progMapper));
        // 日期的时分秒的调整、工期的校验
        bp.addBeforeProcessor(new PlanDateAdjustAndCheck());
        //  调整每层上级的开始日期、结束日期
        bp.addAfterProcessor(new PlanAdjustUpperProcessor(parent, mapper));
        //  图片保存
        bp.insert(plan);
        picService.insertPictureForPub(plan, new PictureParamVO(null, null, BillTypeConst.PROCESS_PLAN_BILLTYPE));
        return plan.getPkPlan();
    }

    @Override
    public String updatePlan(ProjectPlanVO plan) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + plan.getPkProject())) {
            throw new ConcurrentOperationException();
        }
        ProjectPlanVO orig = mapper.getById(plan.getPkPlan());
        // 查询是否为叶子节点
        SingleUpdateBpTemplate<ProjectPlanVO> bp = null;
        if (orig.getChildCount() > 0) {
            // 非末级计划，日期不可修改,可以不校验
            bp = new SingleUpdateBpTemplate<>(mapper, UPDATE_FIELDS);
        } else {
            bp = new SingleUpdateBpTemplate<>(mapper, UPDATE_LEAF_FIELDS);
            String parentId = plan.getPkParent();
            ProjectPlanVO parent = null;
            if (!StringUtils.isEmpty(parentId)) {
                parent = mapper.getById(parentId);
                if (parent == null) {
                    throw new IllegalRequestArgException();
                }
            }
            // 日期的时分秒的调整、工期的校验
            bp.addBeforeProcessor(new PlanDateAdjustAndCheck());
            // 末级计划更新上级开始日期、结束日期，工期校验
            bp.addAfterProcessor(new PlanAdjustUpperProcessor(parent, mapper));
        }
        bp.addBeforeProcessor(new AuditInfoUpdateProcessor<ProjectPlanVO>());
        // 校验是否为创建人
        bp.addBeforeProcessor(new PlanUpdatePermCheck());
        //  图片保存
        bp.update(plan);
        picService.updateBillPicture(plan, new PictureParamVO(null, null, BillTypeConst.PROCESS_PLAN_BILLTYPE));
        return plan.getPkPlan();
    }

    @Override
    public void deletePlan(String id, String changeFlag) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + id)) {
            throw new ConcurrentOperationException();
        }
        ProjectPlanVO orig = mapper.getById(id);
        if (!changeFlag.equals(orig.getChangeFlag())) {
            throw new DataModifiedException();
        }
        if (!StringUtils.trimToEmpty(orig.getCreator()).equals(RuntimeEnvironment.getLoginUserId())) {
            throw new BusinessException("当前用户不是该计划的创建人，不可删除该计划！");
        }
        // 查询节点所有子节点
        List<String> children = new ArrayList<>();
        Queue<String> parents = new LinkedList<>();
        parents.add(orig.getPkPlan());
        children.add(orig.getPkPlan());
        do {
            String parent = parents.remove();
            String[] pks = mapper.getChildrenPks(parent);
            if (!ArrayUtils.isEmpty(pks)) {
                children.addAll(Arrays.asList(pks));
                Collections.addAll(parents, pks);
            }
        } while (parents.peek() != null);
        String[] childPks = children.toArray(new String[children.size()]);
        // 删除所有子节点
        for (int i = 0; i < children.size(); i += 100) {
            mapper.deletePlanBatch(ArrayUtils.subarray(childPks, i, i + 100));
        }
        // TODO 暂时不删除图片了。。。
        // 修改父节点的count
        mapper.deceaseChildCount(orig.getPkParent(), 1);
        // 需要递归向上计算更新父节点的日期, 需要重算
        new PlanAdjustUpperProcessor(null, mapper).processDelete(orig);
        int no = orig.getSeq();
        // update 同级的no
        mapper.deceaseChildSeqAfterNo(orig.getPkParent(), orig.getPkProject(), no);

        //将上一版本修改为最新状态
        ProjectPlanVO[] prePlanVOS = mapper.queryPlanBySrcAndVersion(orig.getPkSrc(), orig.getVersion() - 1);
        if(prePlanVOS != null){
            SingleUpdateBpTemplate<ProjectPlanVO> bp = new SingleUpdateBpTemplate<>(mapper, UPDATE_LASTFLAG_FIELD);
            for(ProjectPlanVO vo : prePlanVOS){
                vo.setLastFlag("Y");
                bp.update(vo);
            }
        }

        // 如果存在失败调用审批的记录，则删除失败记录
        FailedExternalRecordVO recordVO = recordQuery.queryByPkBill(id);
        if(recordVO != null){
            recordMaintain.delete(recordVO);
        }

    }

    @Override
    public int updatePlanProgress(PlanProgressVO[] progresses) throws BusinessException {
        PlanProgressVO[] afterVOs = processProgressVO(progresses);

        // 记录变更历史
        PlanProgressHistoryVO[] histories = processProgressHistories(afterVOs, null);
        return histories.length;
    }


    @Override
    //@PMApprove
    public PlanWriteVO updatePlanProgressFull(PlanWriteVO planWriteVO) throws BusinessException {
        PlanProgressTransVO[] trans= new PlanProgressTransVO[0];
        RuntimeEnvironment.setTenantId(planWriteVO.getPkTenant());
        if (planWriteVO!=null){
            trans = planWriteVO.getProgresse();
        }

        if (ArrayUtils.isEmpty(trans)) {
            return null;
        }
        PlanProgressVO[] progresses = new PlanProgressVO[trans.length];
        for (int i = 0; i < progresses.length; i++) {
            progresses[i] = PlanProgressVO.convertFromTransVO(trans[i]);
        }
        progresses = this.processProgressVO(progresses);

        //添加进度填报
        ProjectPlanVO projectPlanVO = mapper.queryByPkPlan(progresses[0].getPkPlan());

        planWriteVO.setPkProject(projectPlanVO.getPkProject());
        planWriteVO.setPkTenant(projectPlanVO.getPkTenant());
        planWriteVO.setPkPlan(projectPlanVO.getPkRoot());
        planWriteVO.setBillType(BillTypeConst.PROCESS_WRITE_BILLTYPE);
        planWriteVO.setWriter(RuntimeEnvironment.getLoginUserId());
        planWriteVO.setWriteDate(new Date());
        planWriteVO.setBillstatus(BillStatusConst.COMMON_FREE_STATUS);
        planWriteVO = planWriteMaintain.savePlanWrite(planWriteVO);


        Map<String, PlanProgressTransVO> tranMap = new HashMap<>(trans.length);
        for (PlanProgressTransVO tran : trans) {
            tran.setPkPlanwrite(planWriteVO.getPkPlanwrite());
            tran.setPkProject(planWriteVO.getPkProject());
            tranMap.put(tran.getPkPlan(), tran);
        }
        //存储反馈记录
        PlanProgressHistoryVO[] planProgressHistoryVOS = processProgressHistories(progresses, tranMap);
        // 驱动计划
        drivePlan(progresses);

        return planWriteVO;
    }

    /**
     * 进度完成，驱动计划完成
     *
     * @param progresses
     */
    private void drivePlan(PlanProgressVO[] progresses) throws BusinessException {
        Set<String> pkPlanSet = new HashSet<>();
        Map<String, PlanProgressVO> planToProgress = new HashMap<>();
        for (PlanProgressVO progressVO : progresses) {
            // 进度为100的表示进度完成
            if (progressVO.getProgress() != null && progressVO.getProgress().equals(100)) {
                pkPlanSet.add(progressVO.getPkPlan());
                planToProgress.put(progressVO.getPkPlan(), progressVO);
            }
        }

        // 查询填报的计划
        if(pkPlanSet == null || pkPlanSet.size() == 0) return;
        List<ProjectPlanVO> leafPlans = mapper.queryByPkPlans(pkPlanSet.toArray(new String[0]));
        if (CollectionUtils.isEmpty(leafPlans))
            return;

        // 设置计划完成人，获取要驱动的计划
        List<ProjectPlanVO> planDrivenList = new ArrayList<>();
        for (ProjectPlanVO planVO : leafPlans) {
            // 关联单据的计划、已完成的计划不驱动
            boolean notDrive = StringUtils.isNotEmpty(planVO.getRelatedBillType()) ||
                    planVO.getCompletedStatus() == PlanProgressStatusConst.PLAN_COMPLETED;
            if (notDrive)
                continue;

            PlanProgressVO progressVO = planToProgress.get(planVO.getPkPlan());
            if (progressVO != null) {
                planVO.setPlanFinisher(StringUtils.isEmpty(progressVO.getModifier()) ? progressVO.getCreator() : progressVO.getModifier());
                planDrivenList.add(planVO);
            }
        }

        // 驱动计划
        planDriven.drivePlansByProgress(planDrivenList.toArray(new ProjectPlanVO[0]));
    }

    private PlanProgressVO[] processProgressVO(PlanProgressVO[] progresses) throws IllegalRequestArgException {

        // 更新计划的进度
        Map<String, PlanProgressVO> planIds = new HashMap<>(progresses.length);
        for (PlanProgressVO progress : progresses) {
            planIds.put(progress.getPkPlan(), progress);
        }
        for (PlanProgressVO progress : progresses) {
            Date endDate = progress.getEndDate();
            if (endDate != null &&
                    (progress.getStartDate() == null || progress.getStartDate().after(endDate))) {
                throw new IllegalRequestArgException();
            }
        }
        //给实际结束时间赋值
        if (ArrayUtils.isNotEmpty(progresses)){
            for (PlanProgressVO planProgressVO:progresses){
                if(planProgressVO.getEndDate()==null){
                    planProgressVO.setEndDate(new Date());
                }
            }
        }
        PlanProgressVO[] storedProgs = progMapper.queryByPlanIds(planIds.keySet().toArray(new String[0]));
        if (storedProgs == null) {
            storedProgs = new PlanProgressVO[0];
        }

        for (PlanProgressVO progress : storedProgs) {
            // 移除待更新的进度，保留待插入的计划
            PlanProgressVO newProg = planIds.remove(progress.getPkPlan());
            if (newProg != null) {
                progress.setProgress(newProg.getProgress());
                progress.setStartDate(newProg.getStartDate());
                progress.setEndDate(newProg.getEndDate());
            }
        }
        // 更新计划
        // 这不对changFlag处理,没有很大的必要
        for (PlanProgressVO progress : storedProgs) {
            progress.setChangeFlag(UUID.randomUUID().toString());
            progress.setModifier(RuntimeEnvironment.getLoginUserId());
            progress.setModifytime(new Date());
            progress.setLastSubmitDate(progress.getModifytime());
            progMapper.updatePlanProgress(progress);
        }
        // 插入计划
        PlanProgressVO[] insertVOs = planIds.values().toArray(new PlanProgressVO[0]);
        for (PlanProgressVO progress : insertVOs) {
            progress.setChangeFlag(UUID.randomUUID().toString());
            progress.setCreator(RuntimeEnvironment.getLoginUserId());
            progress.setCreationtime(new Date());
            progress.setFirstSubmitDate(progress.getCreationtime());
            progress.setLastSubmitDate(progress.getCreationtime());
            progMapper.insert(progress);
        }
        return ArrayUtils.addAll(storedProgs, insertVOs);
    }

    private PlanProgressHistoryVO[] processProgressHistories(PlanProgressVO[] afterVOs, Map<String, PlanProgressTransVO> tranMap) throws BusinessException {
        PlanProgressHistoryVO[] histories = new PlanProgressHistoryVO[afterVOs.length];
        for (int i = 0; i < afterVOs.length; i++) {
            histories[i] = PlanProgressHistoryVO.convertFromProgress(afterVOs[i]);
        }
        for (PlanProgressHistoryVO history : histories) {
            if (tranMap != null) {
                PlanProgressTransVO trans = tranMap.get(history.getPkPlan());
                if (trans != null) {
                    history.setPkPlanwrite(trans.getPkPlanwrite());
                    history.setDescription(trans.getDescription());
                    history.setPictures(trans.getPictures());
                }
            }
            history.setPkProgressHis(UUID.randomUUID().toString());
            history.setChangeFlag(UUID.randomUUID().toString());
            history.setDr((byte) 0);
            int insert = hisMapper.insert(history);

            ArrayList<PlanIndexVO> planIndexVOS = new ArrayList<>();
            //插入指标记录
            if(tranMap != null){
                PlanProgressTransVO transVO = tranMap.get(history.getPkPlan());
                if(transVO != null && transVO.getMeasureIndexHistoryVOS() != null){
                    MeasureIndexHistoryVO[] measureIndexHistoryVOS = transVO.getMeasureIndexHistoryVOS();
                    //记录指标总数
                    ProjectPlanVO projectPlanVO = new ProjectPlanVO();
                    projectPlanVO.setPkPlan(transVO.getPkPlan());
                    projectPlanVO.setIndexNumber(measureIndexHistoryVOS.length);
                    mapper.updateFieldsWithoutFlag(projectPlanVO,new String[]{ProjectPlanVO.INDEX_NUMBER});

                    for (MeasureIndexHistoryVO measureIndexHistoryVO : measureIndexHistoryVOS) {
                        measureIndexHistoryVO.setPkProgressHis(history.getPkProgressHis());
                        //封装计划指标信息
                        if(measureIndexHistoryVO.getPkPlanIndex() == null){
                            //表示web端没有设置关联 该指标是新添加计划指标
                            PlanIndexVO planIndexVO = new PlanIndexVO();
                            planIndexVO.setPkProject(transVO.getPkProject());
                            planIndexVO.setPkMeasureIndex(measureIndexHistoryVO.getPkMeasureindex());
                            planIndexVO.setPkPlan(transVO.getPkPlan());
                            planIndexVO.setExpectTotal(measureIndexHistoryVO.getExpectTotal());
                            planIndexVOS.add(planIndexVO);
                        }
                    }
                    //插入计划指标关联信息
                    if(planIndexVOS != null && !planIndexVOS.isEmpty()) {
                        List<PlanIndexVO> save = planIndexMaintain.save(planIndexVOS, null);
                        //获取主键pkPlanIndex 并给指标记录对象赋值
                        HashMap<String, String> pkPlanIndexMap = new HashMap<>();
                        for (PlanIndexVO planIndexVO : save) {
                            pkPlanIndexMap.put(planIndexVO.getPkMeasureIndex(), planIndexVO.getPkPlanIndex());
                        }
                        for (MeasureIndexHistoryVO measureIndexHistoryVO : measureIndexHistoryVOS) {
                            measureIndexHistoryVO.setPkPlanIndex(pkPlanIndexMap.get(measureIndexHistoryVO.getPkMeasureindex()));
                        }
                    }

                    measureIndexHisMaintin.insert(measureIndexHistoryVOS);
                }
            }
        }
        // 存储图片路径
        for (PlanProgressHistoryVO history : histories) {
            picService.updateBillPicture(history, new PictureParamVO(null, null, BillTypeConst.PROCESS_REPORT_BILLTYPE));
        }
        return histories;
    }

    @Override
    public String savePlan(ProjectPlanVO plan) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + plan.getPkProject())) {
            throw new ConcurrentOperationException();
        }

        // 校验是否为根计划的创建人
        if(StringUtils.isNotBlank(plan.getPkPlan())) rootCreatorCheck(plan);

        //删除计划
        if(plan.getDeletePlan() != null && plan.getDeletePlan().length > 0){
            deleteBatch(plan.getDeletePlan());
        }

        List<ProjectPlanVO> insertList = new ArrayList<>();
        List<ProjectPlanVO> updateList = new ArrayList<>();
        Map<String, String> codePlanMap = new HashMap<>();

        // 计划关联的指标VO、删除的计划指标主键
        List<PlanIndexVO> saveIndexList = new ArrayList<>();
        List<String> deleteIndexIds = new ArrayList<>();

        //新增根计划时设置版本
        if(StringUtils.isBlank(plan.getPkPlan())){
            plan.setVersion(1);
            plan.setLastFlag("Y");
        }
        genplan(null, plan, insertList, updateList, codePlanMap, SAVE_ACTION, null,
                saveIndexList, deleteIndexIds);//递归遍历计划树
        CheckProgress(insertList);//校验新增节点的上级进度是否为空
        buildDependPlan(insertList, updateList, codePlanMap);//映射前置计划
        if(!insertList.isEmpty()){
            MultiInsertBpTemplate<ProjectPlanVO> bp = new MultiInsertBpTemplate<ProjectPlanVO>(mapper, false);
            bp.addBeforeProcessor(new AuditInfoInsertMultiProcessor<ProjectPlanVO>());
            bp.insert(insertList.toArray(new ProjectPlanVO[0]));
        }
        if(!updateList.isEmpty()){
            SingleUpdateBpTemplate<ProjectPlanVO> bp = new SingleUpdateBpTemplate<ProjectPlanVO>(mapper, UPDATE_ALL_FIELDS);
            bp.addBeforeProcessor(new AuditInfoUpdateProcessor<ProjectPlanVO>());
            for(ProjectPlanVO vo : updateList){
                bp.update(vo);
            }
        }

        // 更新计划指标
        planIndexMaintain.save(saveIndexList, deleteIndexIds);
        return plan.getPkPlan();
    }

    @Override
    public void savePlanAuto(String projectName,String pkProject,String pkTenant, String pkOrg, String type) throws BusinessException {
        OrganizationVO organizationVO = organizationQuery.queryByPkSrc(pkTenant,pkOrg);
        if(null==organizationVO){
            return;
        }
        String pkOrganization = organizationVO.getPkOrganization();//组织id
        String organiationName = organizationVO.getSname();//组织名称

        PlanTempletVO[] templetVOS = planTempletMapper.autoSearchPlanTemp(pkTenant,pkOrganization,type);
        if(null == templetVOS || templetVOS.length == 0){
            return;
        }
        Map<String, List<PlanTempletVO>> childMap = genChildMap(templetVOS);

        List<PlanTempletVO> planTempletVOS = childMap.get(StringUtils.EMPTY);

        if(null != planTempletVOS && planTempletVOS.size() > 0){
            for(PlanTempletVO rootNode : planTempletVOS){
                //计划容器
                ProjectPlanVO temp = new ProjectPlanVO();
                temp.setPkProject(pkProject);
                //Sname->组织名称_项目名称_计划类型_模板名称
                ProjectTypeVO projectTypeVO = projectTypeQuery.queryById(type);
                StringBuilder builder = new StringBuilder();
                nameSplicing(projectName, organiationName, rootNode, projectTypeVO, builder);
                temp.setSname(builder.toString());
                temp.setScode(rootNode.getScode());
                temp.setTempletVO(rootNode);
                Map<String, String> planMap = new HashMap<>();
                buildPlan(rootNode, temp, planMap);
                //默认审核通过(生效)在server端更改，在这里做会被覆盖
                //保存
                autoSavePlan(temp);
            }
        }
    }

    /**
     * 自动生成计划命名规范拼接
     * @param projectName
     * @param organiationName
     * @param rootNode
     * @param projectTypeVO
     * @param builder
     */
    private void nameSplicing(String projectName, String organiationName, PlanTempletVO rootNode, ProjectTypeVO projectTypeVO, StringBuilder builder) {
        if(StringUtils.isNotEmpty(organiationName)){
            builder.append(organiationName);
        }
        if(StringUtils.isNotEmpty(projectName)){
            builder.append("_").append(projectName);
        }
        if(StringUtils.isNotEmpty(projectTypeVO.getSname())){
            builder.append("_").append(projectTypeVO.getSname());
        }
        if(StringUtils.isNotEmpty(rootNode.getSname())){
            builder.append("_").append(rootNode.getSname());
        }
    }

    private void buildPlan(PlanTempletVO tempVO, ProjectPlanVO planVO, Map<String, String> planMap) throws BusinessException {
        if(tempVO.getChildren() != null){
            for(PlanTempletVO vo : tempVO.getChildren()){
                ProjectPlanVO pro = new ProjectPlanVO();
                convert(planVO, pro, vo, planMap);
                buildPlan(vo, pro, planMap);
            }
        }
    }

    private void convert(ProjectPlanVO parent, ProjectPlanVO child, PlanTempletVO tempVO, Map<String, String> planMap) throws BusinessException {
        //设置上级计划childCount
        int childCount = parent.getChildCount() == null ? 0 : parent.getChildCount();
        parent.setChildCount(childCount + 1);
        //构建子计划
        child.setPkProject(parent.getPkProject());
        child.setSname(tempVO.getSname());
        if(StringUtils.isBlank(parent.getScode()) || parent.getScode().equals("0")){
            child.setScode(parent.getChildCount().toString());
        }else{
            child.setScode(parent.getScode()+"."+parent.getChildCount());
        }
        child.setSeq(parent.getChildCount());
        if (tempVO.getExpectDuration() != null) {
            child.setExpeDuration(tempVO.getExpectDuration());
            child.setExpeStartDate(formatDate(new Date()));
            child.setExpeEndDate(formatDate(org.apache.commons.lang3.time.DateUtils.addDays(new Date(), tempVO.getExpectDuration() - 1)));
        } else {
            child.setExpeDuration(1);
            child.setExpeStartDate(formatDate(new Date()));
            child.setExpeEndDate(formatDate(new Date()));
        }

        child.setRelatedBillType(tempVO.getRelatedBillType());
        child.setManager(tempVO.getManager());
        child.setManagerName(tempVO.getManagerName());
        child.setPkMilestone(tempVO.getPkMilestone());
        child.setMilestoneName(tempVO.getMilestoneName());
        child.setRelatedBillType(tempVO.getRelatedBillType());
        child.setRelatedBillTypeName(tempVO.getRelatedBillTypeName());
        child.setPkDepartment(tempVO.getPkDepartment());
        child.setDepartmentName(tempVO.getDepartmentName());

        planMap.put(tempVO.getPkPlanTemplet(), child.getScode());
        child.setDependPlanName(tempVO.getDependTemplet());

        ProjectPlanVO[] childs = parent.getChildren();
        if(childs == null) childs = new ProjectPlanVO[0];
        parent.setChildren(ArrayUtils.add(childs, child));

    }

    private Map<String, List<PlanTempletVO>> genChildMap(PlanTempletVO[] allVOs) {
        // 构造树
        Map<String, List<PlanTempletVO>> childMap = new HashMap<>();
        for (PlanTempletVO vo : allVOs) {
            vo.setKey(vo.getPkPlanTemplet());
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            }
            List<PlanTempletVO> children = childMap.get(parent);
            if (children == null) {
                children = new ArrayList<>();
                childMap.put(parent, children);
            }
            children.add(vo);
        }
        for (PlanTempletVO vo : allVOs) {
            List<PlanTempletVO> children = childMap.get(vo.getPkPlanTemplet());
            if (children != null) {
                vo.setChildren(children.toArray(new PlanTempletVO[0]));
            }
        }
        return childMap;
    }

    @Override
    public String autoSavePlan(ProjectPlanVO plan) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + plan.getPkProject())) {
            throw new ConcurrentOperationException();
        }

        // 校验是否为根计划的创建人
        if(StringUtils.isNotBlank(plan.getPkPlan())) rootCreatorCheck(plan);

        //删除计划
        if(plan.getDeletePlan() != null && plan.getDeletePlan().length > 0){
            deleteBatch(plan.getDeletePlan());
        }

        List<ProjectPlanVO> insertList = new ArrayList<>();
        List<ProjectPlanVO> updateList = new ArrayList<>();
        Map<String, String> codePlanMap = new HashMap<>();

        // 计划关联的指标VO、删除的计划指标主键
        List<PlanIndexVO> saveIndexList = new ArrayList<>();
        List<String> deleteIndexIds = new ArrayList<>();

        //新增根计划时设置版本
        if(StringUtils.isBlank(plan.getPkPlan())){
            plan.setVersion(1);
            plan.setLastFlag("Y");
        }
        autoGenplan(null, plan, insertList, updateList, codePlanMap, SAVE_ACTION, null,
                saveIndexList, deleteIndexIds);//递归遍历计划树
        CheckProgress(insertList);//校验新增节点的上级进度是否为空
        buildDependPlan(insertList, updateList, codePlanMap);//映射前置计划
        if(!insertList.isEmpty()){
            MultiInsertBpTemplate<ProjectPlanVO> bp = new MultiInsertBpTemplate<ProjectPlanVO>(mapper, false);
            bp.addBeforeProcessor(new AuditInfoInsertMultiProcessor<ProjectPlanVO>());
            bp.insert(insertList.toArray(new ProjectPlanVO[0]));
        }
        if(!updateList.isEmpty()){
            SingleUpdateBpTemplate<ProjectPlanVO> bp = new SingleUpdateBpTemplate<ProjectPlanVO>(mapper, UPDATE_ALL_FIELDS);
            bp.addBeforeProcessor(new AuditInfoUpdateProcessor<ProjectPlanVO>());
            for(ProjectPlanVO vo : updateList){
                bp.update(vo);
            }
        }

        return plan.getPkPlan();
    }

    @Override
    public void updatePlanforAdjust(ProjectPlanVO plan) throws BusinessException {
        // lock project
        if (!AutoReleaseLockUtil.addAutoReleaseLock(LOCK_PREFIX + plan.getPkProject())) {
            throw new ConcurrentOperationException();
        }

        // 校验是否为根计划的创建人
        if(StringUtils.isNotBlank(plan.getPkPlan())) rootCreatorCheck(plan);

        ProjectPlanVO[] origPlanVOS = mapper.queryPlanByRoot(plan.getPkPlan());

        if(origPlanVOS != null && origPlanVOS.length > 0){
            //构建末级计划与进度关系
            Map<String, PlanProgressVO> idProgressMap = new HashMap<>();
            Map<String, List<PlanIndexVO>> planToIndexMap = new HashMap<>();
            buildIDProgressAndIndexMap(origPlanVOS, idProgressMap, planToIndexMap);

            //新版本版本号加一 设置最新标志
            plan.setVersion(origPlanVOS[0].getVersion() + 1);
            plan.setLastFlag("Y");
            plan.setPkSrc(origPlanVOS[0].getPkSrc());

            List<ProjectPlanVO> insertList = new ArrayList<>();
            Map<String, String> codePlanMap = new HashMap<>();
            Map<String, String> origPlanMap = new HashMap<>();//新旧主键映射

            // 计划当前关联的指标VO
            List<PlanIndexVO> saveIndexList = new ArrayList<>();

            genplan(null, plan, insertList, null, codePlanMap, ADJUST_ACTION, origPlanMap, saveIndexList, null);//递归遍历计划树
            buildDependPlan(insertList, null, codePlanMap);//映射前置计划
            if(!insertList.isEmpty()){
                MultiInsertBpTemplate<ProjectPlanVO> bp = new MultiInsertBpTemplate<ProjectPlanVO>(mapper, false);
                bp.addBeforeProcessor(new AuditInfoInsertMultiProcessor<ProjectPlanVO>());
                bp.insert(insertList.toArray(new ProjectPlanVO[0]));
            }
            //修改旧版本最新标志
            SingleUpdateBpTemplate<ProjectPlanVO> bp = new SingleUpdateBpTemplate<ProjectPlanVO>(mapper, UPDATE_LASTFLAG_FIELD);
            bp.addBeforeProcessor(new AuditInfoUpdateProcessor<ProjectPlanVO>());
            for(ProjectPlanVO vo : origPlanVOS){
                vo.setLastFlag("N");
                bp.update(vo);
            }
            //同步计划进度至新版本
            if(origPlanMap !=null && idProgressMap != null){
                List<PlanProgressVO> progressVOList = new ArrayList<>();
                for(String key : origPlanMap.keySet()){
                    if(idProgressMap.get(key) != null){
                        PlanProgressVO progressVO = idProgressMap.get(key);
                        progressVO.setPkPlan(origPlanMap.get(key));
                        progressVOList.add(progressVO);
                    }
                }
                if(!progressVOList.isEmpty()){
                    MultiInsertBpTemplate<PlanProgressVO> progressBp = new MultiInsertBpTemplate<PlanProgressVO>(progMapper, false);
                    progressBp.insert(progressVOList.toArray(new PlanProgressVO[0]));
                }
            }

            // 同步计划指标
            synIndexesOfOriginPlans(origPlanMap, saveIndexList, planToIndexMap);
        }
    }

    /**
     * 将调整前的计划指标同步到当前计划，如果当前计划存在指标则不同步
     *
     *  @param origPlanMap 调整前计划主键和调整后计划主键映射
     * @param currIndexList 调整时计划下关联的指标
     * @param planToIndexMap 调整前计划关联的历史指标
     */
    private void synIndexesOfOriginPlans(Map<String, String> origPlanMap, List<PlanIndexVO> currIndexList, Map<String, List<PlanIndexVO>> planToIndexMap) throws BusinessException {
        // 要新增的计划指标
        List<PlanIndexVO> savedList = new ArrayList<>();
        // 调整时关联了指标的计划集合
        Set<String> currPlanSet = new HashSet<>();
        if (CollectionUtils.isNotEmpty(currIndexList)) {
            for (PlanIndexVO vo : currIndexList) {
                vo.setPkPlanIndex(null);
                currPlanSet.add(vo.getPkPlan());
            }
        }

        for (Map.Entry<String, String> entry : origPlanMap.entrySet()) {
            String pkNewPlan = entry.getValue();
            // 调整时关联了指标的计划不需要同步
            if (currPlanSet.contains(pkNewPlan))
                continue;

            List<PlanIndexVO> indexVOs = planToIndexMap.get(entry.getKey());
            if (CollectionUtils.isEmpty(indexVOs))
                continue;

            // 同步调整时未关联指标的计划
            for (PlanIndexVO indexVO : indexVOs) {
                indexVO.setPkPlan(entry.getValue());
                // 新增，所以将主键置空
                indexVO.setPkPlanIndex(null);
                savedList.add(indexVO);
            }
        }

        if (CollectionUtils.isNotEmpty(currIndexList))
            savedList.addAll(currIndexList);

        if (savedList.size() > 0)
            planIndexMaintain.save(savedList, null);
    }

    @Override
    @PMApprove(trigger = "commit")
    public ProjectPlanVO commit(ProjectPlanVO plan) throws BusinessException {
        ProjectPlanVO rootVO = mapper.getById(plan.getPkPlan());
        ProjectPlanVO[] planVOS = mapper.queryPlanByRoot(plan.getPkPlan());
        SingleUpdateBpTemplate<ProjectPlanVO> bp = new SingleUpdateBpTemplate<>(mapper, UPDATE_STATUS_FIELD);
        rootVO.setBillstatus(BillStatusConst.COMMON_APPROVING_STATUS);
        for(ProjectPlanVO vo : planVOS){
            if(!StringUtils.equals(vo.getPkRoot(),vo.getPkPlan())) {
                vo.setBillstatus(BillStatusConst.COMMON_APPROVING_STATUS);
                bp.update(vo);
            }
        }

        return rootVO;
    }

    @Override
    public ProjectPlanVO[] updateVOs(ProjectPlanVO[] planVOs) throws BusinessException {
        if (ArrayUtils.isEmpty(planVOs))
            return new ProjectPlanVO[0];

        String[] updateFields = {ProjectPlanVO.COMPLETED_STATUS, ProjectPlanVO.PLAN_FINISHER, ProjectPlanVO.RELATED_PK_BILL};
        SingleUpdateBpTemplate<ProjectPlanVO> bpTemplate = new SingleUpdateBpTemplate<>(mapper, updateFields);
        for (ProjectPlanVO vo : planVOs) {
            bpTemplate.update(vo);
        }

        return planVOs;
    }

    @Override
    @PMApprove
    public ProjectPlanVO addDefaultPorjetPlan(ProjectPlanVO projectPlan) throws BusinessException {
        /**
         *   1.在添加之前校验是否已经有默认的在审批
         *   2.调用添加方法
         *   3.调用审批流
         */

        ProjectPlanVO[] projectPlanVOS = mapper.queryDefaultProjectPlan(projectPlan.getPkProject(), projectPlan.getSname(), projectPlan.getScode());

        if(projectPlanVOS != null && projectPlanVOS.length > 0) {
            //已经添加过默认指标
            throw new BusinessException("已经存在默认指标填报");
        }
        savePlan(projectPlan);

        return projectPlan;
    }

    /**
     * 校验根计划创建人
     * @param plan
     * @throws BusinessException
     */
    private void rootCreatorCheck(ProjectPlanVO plan) throws BusinessException {
        String rootId = plan.getPkRoot();
        ProjectPlanVO root = mapper.getById(rootId);
        String creator = RuntimeEnvironment.getLoginUserId();
        if (plan.getCreator() == null || !creator.equals(plan.getCreator())) {
            throw new BusinessException("当前用户不是该根计划的创建人，不可操作！");
        }
    }

    private void deleteBatch(String[] deletePkPlan){
        String[] allChildPks = new String[0];
        for(String plan : deletePkPlan){
            // 查询节点所有子节点
            List<String> children = new ArrayList<>();
            Queue<String> parents = new LinkedList<>();
            parents.add(plan);
            children.add(plan);
            do {
                String parent = parents.remove();
                String[] pks = mapper.getChildrenPks(parent);
                if (!ArrayUtils.isEmpty(pks)) {
                    children.addAll(Arrays.asList(pks));
                    Collections.addAll(parents, pks);
                }
            } while (parents.peek() != null);
            String[] childPks = children.toArray(new String[children.size()]);
            allChildPks = ArrayUtils.addAll(allChildPks, childPks);
        }
        // 删除所有子节点
       if(allChildPks.length > 0) mapper.deletePlanBatch(allChildPks);
    }

    //递归计划树，计划字段赋值，方法有点长，后期需要优化
    private void genplan(ProjectPlanVO parent, ProjectPlanVO plan, List<ProjectPlanVO> insertList, List<ProjectPlanVO> updateList, Map<String, String> codePlanMap, int action, Map<String, String> origPlanMap, List<PlanIndexVO> saveIndexList, List<String> deleteIndexIds) throws BusinessException {
        //非根计划校验必输项
        if(parent != null) checkPlan(plan);
        //确定是否为新增计划
        boolean isNewTree = true;
        //生成主键
        String id = UUID.randomUUID().toString();

        if(action == SAVE_ACTION){//保存
            if(StringUtils.isNotBlank(plan.getPkPlan())) isNewTree = false;
            else plan.setPkPlan(id);
        } else if(action == ADJUST_ACTION){//调整
            if(StringUtils.isNotBlank(plan.getPkPlan())) {
                origPlanMap.put(plan.getPkPlan(), id);
                plan.setHistoryPlan(plan.getPkPlan());
            }
            plan.setPkPlan(id);
        }

        //配置计划编码和主键的映射
        codePlanMap.put(plan.getScode(), plan.getPkPlan());
        //层级设置pk_parent和pk_root
        if(isNewTree){
            if(parent == null) {
                plan.setPkRoot(plan.getPkPlan());
                //首版本设置来源主键为根计划主键
                if(plan.getVersion() == 1) plan.setPkSrc(plan.getPkPlan());
            }else{
                plan.setPkProject(parent.getPkProject());
                plan.setPkRoot(parent.getPkRoot());
                plan.setPkParent(parent.getPkPlan());
                plan.setPkSrc(parent.getPkSrc());
                plan.setVersion(parent.getVersion());
                plan.setLastFlag(parent.getLastFlag());
            }
        }

        //根计划设置顺序
        if(parent == null && isNewTree) {
            int no = mapper.countRootPlanOfProject(plan.getPkProject());
            plan.setSeq(no + 1);
        }
        //子计划节点顺序(默认取编码最后一位)
        if(parent != null && StringUtils.isNotBlank(plan.getScode())){
            String[] codes = plan.getScode().split("\\.");
            if(codes != null && codes.length > 0) plan.setSeq(Integer.valueOf(codes[codes.length - 1]));
        }

        //设置租户
        plan.setPkTenant(RuntimeEnvironment.getTenantId());
        //设置单据状态为自由态
        if(isNewTree){
            plan.setBillstatus(BillStatusConst.COMMON_FREE_STATUS);
        }
        //设置单据类型
        plan.setBillType(BillTypeConst.PROCESS_PLAN_BILLTYPE);

        if(plan.getChildren()!=null && plan.getChildren().length > 0){
            //设置子节点数
            plan.setChildCount(plan.getChildren().length);
            // 设置指标数量
            plan.setIndexNumber(0);
            //调整每层上级的开始结束日期工期
            Date minStartDate = null, maxEndDate = null;
            for(ProjectPlanVO vo : plan.getChildren()){
                genplan(plan, vo, insertList, updateList, codePlanMap, action, origPlanMap, saveIndexList, deleteIndexIds);
                Date expeStartDate = DateUtils.asDateBegin(formatDate(vo.getExpeStartDate()));
                Date expeEndDate = DateUtils.asDateEnd(formatDate(vo.getExpeEndDate()));
                if(minStartDate == null || expeStartDate.before(minStartDate)){
                    minStartDate = expeStartDate;
                }
                if(maxEndDate == null || expeEndDate.after(maxEndDate)){
                    maxEndDate = expeEndDate;
                }
            }
            if(minStartDate != null && maxEndDate != null){
                plan.setExpeStartDate(minStartDate);
                plan.setExpeEndDate(maxEndDate);
                plan.setExpeDuration((int)DateUtils.getDaysBetween(minStartDate, maxEndDate));
            }
        }else{
            //校验末级节点层级
            planLevelCheck(plan);
            //末级节点设置子计划数为0
            plan.setChildCount(0);
            // 获取关联的指标
            getPlanIndexes(plan, action, saveIndexList, deleteIndexIds);
            //设置计划开始完成日期及工期
            if(plan.getExpeStartDate() != null && plan.getExpeEndDate() != null){
                plan.setExpeStartDate(DateUtils.asDateBegin(formatDate(plan.getExpeStartDate())));
                plan.setExpeEndDate(DateUtils.asDateEnd(formatDate(plan.getExpeEndDate())));
                int days = (int) DateUtils.getDaysBetween(plan.getExpeStartDate(), plan.getExpeEndDate());
                plan.setExpeDuration(days);
            }else{
                plan.setExpeStartDate(new Date());
                plan.setExpeEndDate(new Date());
                plan.setExpeDuration(1);
            }
        }

        if(isNewTree){
            if(insertList != null) insertList.add(plan);
        } else{
            if(updateList != null) updateList.add(plan);
        }
    }

    //递归计划树，计划字段赋值，方法有点长，后期需要优化
    private void autoGenplan(ProjectPlanVO parent, ProjectPlanVO plan, List<ProjectPlanVO> insertList, List<ProjectPlanVO> updateList, Map<String, String> codePlanMap, int action, Map<String, String> origPlanMap, List<PlanIndexVO> saveIndexList, List<String> deleteIndexIds) throws BusinessException {
        //非根计划校验必输项
        if(parent != null) checkPlan(plan);
        //确定是否为新增计划
        boolean isNewTree = true;
        //生成主键
        String id = UUID.randomUUID().toString();

        if(action == SAVE_ACTION){//保存
            if(StringUtils.isNotBlank(plan.getPkPlan())) isNewTree = false;
            else plan.setPkPlan(id);
        } else if(action == ADJUST_ACTION){//调整
            if(StringUtils.isNotBlank(plan.getPkPlan())) {
                origPlanMap.put(plan.getPkPlan(), id);
                plan.setHistoryPlan(plan.getPkPlan());
            }
            plan.setPkPlan(id);
        }

        //配置计划编码和主键的映射
        codePlanMap.put(plan.getScode(), plan.getPkPlan());
        //层级设置pk_parent和pk_root
        if(isNewTree){
            if(parent == null) {
                plan.setPkRoot(plan.getPkPlan());
                //首版本设置来源主键为根计划主键
                if(plan.getVersion() == 1) plan.setPkSrc(plan.getPkPlan());
            }else{
                plan.setPkProject(parent.getPkProject());
                plan.setPkRoot(parent.getPkRoot());
                plan.setPkParent(parent.getPkPlan());
                plan.setPkSrc(parent.getPkSrc());
                plan.setVersion(parent.getVersion());
                plan.setLastFlag(parent.getLastFlag());
            }
        }

        //根计划设置顺序
        if(parent == null && isNewTree) {
            int no = mapper.countRootPlanOfProject(plan.getPkProject());
            plan.setSeq(no + 1);
        }
        //子计划节点顺序(默认取编码最后一位)
        if(parent != null && StringUtils.isNotBlank(plan.getScode())){
            String[] codes = plan.getScode().split("\\.");
            if(codes != null && codes.length > 0) plan.setSeq(Integer.valueOf(codes[codes.length - 1]));
        }

        //设置租户
        plan.setPkTenant(RuntimeEnvironment.getTenantId());
        //设置单据状态为审核通过（自动生成的项目计划状态统一为审核通过）
        if(isNewTree){
            plan.setBillstatus(PlanBillStatus.STATUS_EFFECTIVE);
        }
        //设置单据类型
        plan.setBillType(BillTypeConst.PROCESS_PLAN_BILLTYPE);

        if(plan.getChildren()!=null && plan.getChildren().length > 0){
            //设置子节点数
            plan.setChildCount(plan.getChildren().length);
            // 设置指标数量
            plan.setIndexNumber(0);
            //调整每层上级的开始结束日期工期
            Date minStartDate = null, maxEndDate = null;
            for(ProjectPlanVO vo : plan.getChildren()){
                autoGenplan(plan, vo, insertList, updateList, codePlanMap, action, origPlanMap, saveIndexList, deleteIndexIds);
                Date expeStartDate = DateUtils.asDateBegin(formatDate(vo.getExpeStartDate()));
                Date expeEndDate = DateUtils.asDateEnd(formatDate(vo.getExpeEndDate()));
                if(minStartDate == null || expeStartDate.before(minStartDate)){
                    minStartDate = expeStartDate;
                }
                if(maxEndDate == null || expeEndDate.after(maxEndDate)){
                    maxEndDate = expeEndDate;
                }
            }
            if(minStartDate != null && maxEndDate != null){
                plan.setExpeStartDate(minStartDate);
                plan.setExpeEndDate(maxEndDate);
                plan.setExpeDuration((int)DateUtils.getDaysBetween(minStartDate, maxEndDate));
            }
        }else{
            //校验末级节点层级
            planLevelCheck(plan);
            //末级节点设置子计划数为0
            plan.setChildCount(0);
            // 获取关联的指标
            getPlanIndexes(plan, action, saveIndexList, deleteIndexIds);
            //设置计划开始完成日期及工期
            if(plan.getExpeStartDate() != null && plan.getExpeEndDate() != null){
                plan.setExpeStartDate(DateUtils.asDateBegin(formatDate(plan.getExpeStartDate())));
                plan.setExpeEndDate(DateUtils.asDateEnd(formatDate(plan.getExpeEndDate())));
                int days = (int) DateUtils.getDaysBetween(plan.getExpeStartDate(), plan.getExpeEndDate());
                plan.setExpeDuration(days);
            }else{
                plan.setExpeStartDate(new Date());
                plan.setExpeEndDate(new Date());
                plan.setExpeDuration(1);
            }
        }

        if(isNewTree){
            if(insertList != null) insertList.add(plan);
        } else{
            if(updateList != null) updateList.add(plan);
        }
    }



    /**
     * 获取计划下的指标
     *
     * @param plan
     * @param action
     * @param saveIndexList
     * @param deleteIndexIds
     */
    private void getPlanIndexes(ProjectPlanVO plan, int action, List<PlanIndexVO> saveIndexList, List<String> deleteIndexIds) {
        if (plan.getIndexNumber() == null)
            plan.setIndexNumber(0);

        // 获取计划关联的指标
        List<PlanIndexVO> planIndexVOList = plan.getPlanIndexVOs();
        if (CollectionUtils.isNotEmpty(planIndexVOList)) {
            for (PlanIndexVO indexVO : planIndexVOList) {
                indexVO.setPkPlan(plan.getPkPlan());
                indexVO.setPkProject(plan.getPkProject());
                if (ADJUST_ACTION == action)
                    indexVO.setPkPlanIndex(null);

                if (saveIndexList != null)
                    saveIndexList.add(indexVO);
            }
        }

        // 获取删除的指标
        List<String> deletedPks = plan.getDeletePlanIndexes();
        if (SAVE_ACTION == action && CollectionUtils.isNotEmpty(deletedPks) && deleteIndexIds != null)
            deleteIndexIds.addAll(deletedPks);
    }

    /**
     * 计划校验
     * @param plan
     * @throws BusinessException
     */
    private void checkPlan(ProjectPlanVO plan) throws BusinessException {
        if(StringUtils.isBlank(plan.getSname())){
            throw new BusinessException("计划名称不能为空");
        } else if(plan.getSname().length() > 50){
            throw new BusinessException("计划名称长度不能超过50字");
        }
        if(plan.getExpeStartDate() == null || plan.getExpeStartDate() == null){
            throw new BusinessException("计划开始结束时间不能为空");
        }
    }

    /**
     * 计划层级校验
     * @param plan
     * @throws BusinessException
     */
    private void planLevelCheck(ProjectPlanVO plan) throws BusinessException {
        String code = plan.getScode();
        int level = 1;
        for (int i = 0; i < code.length(); i++) {
            if (code.charAt(i) == '.') {
                level++;
            }
        }
        if (level > MAX_LEVEL) {
            throw new BusinessException("计划超过最大层级限制！");
        }
    }

    /**
     * 进度校验
     * @param insertList
     * @throws BusinessException
     */
    private void CheckProgress(List<ProjectPlanVO> insertList) throws BusinessException {
        if(insertList.isEmpty()) return;
        List<String> leavesId = new ArrayList<>(insertList.size());
        for(ProjectPlanVO  vo : insertList){
            if(StringUtils.isNotBlank(vo.getPkParent())){
                leavesId.add(vo.getPkParent());
            }
        }
        if(!leavesId.isEmpty()){
            PlanProgressVO[] progressVOs = progMapper.queryByPlanIds(leavesId.toArray(new String[0]));
            for(PlanProgressVO prog : progressVOs){
                if (prog != null && prog.getProgress() != 0) {
                    throw new BusinessException("当前计划项的进度反馈已大于0%，不允许增加下级");
                }
            }
        }
    }

    /**
     * 根据前置计划code与主键映射关系赋值前置计划
     * @param insertList
     * @param updateList
     * @param codePlanMap
     * @throws BusinessException
     */
    private void buildDependPlan(List<ProjectPlanVO> insertList, List<ProjectPlanVO> updateList, Map<String, String> codePlanMap) throws BusinessException {
        if(insertList != null){
            for (ProjectPlanVO vo : insertList) {
                if (StringUtils.isNotBlank(vo.getDependPlanName()) && codePlanMap != null) {
                    vo.setDependPlan(codePlanMap.get(vo.getDependPlanName()));
                }
            }
        }
        if(updateList != null){
            for (ProjectPlanVO vo : updateList) {
                if (StringUtils.isNotBlank(vo.getDependPlanName()) && codePlanMap != null) {
                    vo.setDependPlan(codePlanMap.get(vo.getDependPlanName()));
                } else{
                    vo.setDependPlan("");
                }
                if(StringUtils.isBlank(vo.getManager())) vo.setManager("");
            }
        }
    }

    /**
     * 构建末级计划与进度关系映射、计划与指标的映射
     *
     * @param origPlanVOS
     * @param idProgressMap
     * @param planToIndexMap
     */
    private void buildIDProgressAndIndexMap(ProjectPlanVO[] origPlanVOS, Map<String, PlanProgressVO> idProgressMap, Map<String, List<PlanIndexVO>> planToIndexMap) {
        //记录叶子节点
        Set<String> leavesId = new HashSet<>();
        for (ProjectPlanVO vo : origPlanVOS) {
            if (vo.getChildCount() == 0) {
                leavesId.add(vo.getPkPlan());
            }
        }
        //构建末级计划与进度关系
        PlanProgressVO[] progressVOs = progMapper.queryByPlanIds(leavesId.toArray(new String[0]));
        if (!ArrayUtils.isEmpty(progressVOs)) {
            for (PlanProgressVO prog : progressVOs) {
                idProgressMap.put(prog.getPkPlan(), prog);
            }
        }

        // 查询计划关联的历史指标
        Map<String, List<PlanIndexVO>> indexMap = planIndexQuery.queryIndexOfPlans(leavesId.toArray(new String[0]));
        planToIndexMap.putAll(indexMap);
    }

    /**
     * 日期格式化
     * @param date
     * @return
     */
    private Date formatDate(Date date) {
        if (date != null) {
            try {
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                String dateStr = sdf.format(date);
                return sdf.parse(dateStr);
            } catch(ParseException e){
                e.printStackTrace();
            }
        }
        return null;
    }

    private ProjectPlanVO approve(String rootId, byte billStatus) throws BusinessException{
        if(rootId == null) return null;
        ProjectPlanVO rootVO = mapper.getById(rootId);
        ProjectPlanVO[] planVOS = mapper.queryPlanByRoot(rootId);
        SingleUpdateBpTemplate<ProjectPlanVO> bp = new SingleUpdateBpTemplate<>(mapper, UPDATE_STATUS_FIELD);
        if(BillStatusConst.COMMON_APPROVED_STATUS == billStatus){
            for(ProjectPlanVO vo : planVOS){
                vo.setBillstatus(PlanBillStatus.STATUS_EFFECTIVE);
                bp.update(vo);
            }
            //新版本审批通过后将上一版本从生效状态修改至审批通过
            ProjectPlanVO[] prePlanVOS = mapper.queryPlanBySrcAndVersion(rootVO.getPkSrc(), rootVO.getVersion() - 1);
            if(prePlanVOS != null){
                SingleUpdateBpTemplate<ProjectPlanVO> preBp = new SingleUpdateBpTemplate<>(mapper, UPDATE_STATUS_FIELD);
                for(ProjectPlanVO vo : prePlanVOS){
                    vo.setBillstatus(billStatus);
                    preBp.update(vo);
                }
            }
            // 更新历史计划的反馈记录
            updatePkPlanOfProgressHistory(planVOS);
        }else {
            for(ProjectPlanVO vo : planVOS){
                vo.setBillstatus(billStatus);
                bp.update(vo);
            }
        }

            return rootVO;
    }

    /**
     * 更新历史计划的反馈记录，将反馈记录的计划主键设置为现在的计划主键
     * 异步处理
     *
     * @param planVOS
     */
    private void updatePkPlanOfProgressHistory(final ProjectPlanVO[] planVOS) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // <上个版本的计划主键，现在的主键>
                Map<String, String> historyToCurr = new HashMap<>();
                for (ProjectPlanVO projectPlanVO : planVOS) {
                    // 末级计划且存在历史计划
                    if ((projectPlanVO.getChildCount() == null || projectPlanVO.getChildCount() == 0)
                            && StringUtils.isNotEmpty(projectPlanVO.getHistoryPlan())) {
                        historyToCurr.put(projectPlanVO.getHistoryPlan(), projectPlanVO.getPkPlan());
                    }
                }
                if (historyToCurr.size() > 0) {
                    // 查询末级计划节点的所有反馈记录
                    PlanProgressHistoryVO[] oldProgressHistory = hisMapper.queryProgressHistoryByPlans(historyToCurr.keySet().toArray(new String[historyToCurr.size()]));
                    if (ArrayUtils.isNotEmpty(oldProgressHistory)) {
                        SingleUpdateBpTemplate<PlanProgressHistoryVO> historyBp = new SingleUpdateBpTemplate<>(hisMapper, new String[]{ProjectPlanVO.PK_PLAN});
                        for (PlanProgressHistoryVO hisVO : oldProgressHistory) {
                            // 更新反馈记录的计划主键
                            hisVO.setPkPlan(historyToCurr.get(hisVO.getPkPlan()));
                            try {
                                historyBp.update(hisVO);
                            } catch (BusinessException e) {
                                log.error(e.getMessage(), e);
                            }
                        }
                    }
                }
            }
        }).start();
    }

    private ProjectPlanVO reject(String rootId) throws BusinessException{
        ProjectPlanVO rootVO = mapper.getById(rootId);
        ProjectPlanVO[] planVOS = mapper.queryPlanByRoot(rootId);
        SingleUpdateBpTemplate<ProjectPlanVO> bp = new SingleUpdateBpTemplate<>(mapper, UPDATE_STATUS_FIELD);
        for(ProjectPlanVO vo : planVOS){
            vo.setBillstatus(BillStatusConst.COMMON_REJECT_STATUS);
            bp.update(vo);
        }
        return rootVO;
    }

    @Override
    public SuperVO approveByPkBill(String pk_bill, byte billStatus) throws BusinessException {
        return approve(pk_bill, billStatus);
    }

    @Override
    public SuperVO rejectByPkBill(String pk_bill) throws BusinessException {
        return reject(pk_bill);
    }

    @Override
    public SuperVO queryDetailForWorkFlow(String pk_bill) throws BusinessException {
        return mapper.getById(pk_bill);
    }

    @Override
    public String getBillType() {
        return null;
    }

    @Override
    public SuperVO deleteBill(SuperVO businessVO) throws BusinessException {
        SingleDeleteBpTemplate<ProjectPlanVO> deleteBpTemplate = new SingleDeleteBpTemplate<>(mapper);
        return deleteBpTemplate.delete((ProjectPlanVO) businessVO);
    }

    @Override
    public Object getMapper() {
        return null;
    }

    @Override
    public String addOldPlanToSchedule(String tenantId) throws BusinessException {
        ProjectPlanViewVO[] planViewVOS = projectPlanQuery.queryPlanByTenantId(tenantId);

        if(planViewVOS == null || planViewVOS.length <= 0){
            return "该租户计划历史数据查询为空，无需迁移！";
        }
        // 将子计划放到父计划中
        Map<String, List<ProjectPlanViewVO>> childMap = genChildMap(planViewVOS);
        // 获取根节点计划
        List<ProjectPlanViewVO> result = childMap.get(StringUtils.EMPTY);

        List<ScheduleVO> insertScheduleList = new ArrayList<>();
        List<ScheduleItemVO> insertItemVOList = new ArrayList<>();

        Map<String, String> originVersionNameWithPkMap = new HashMap<>();
        for(ProjectPlanViewVO planViewVO : result){
            setOriginVersionPk(originVersionNameWithPkMap, planViewVO);
        }

        for(ProjectPlanViewVO planViewVO : result){
            ScheduleVO scheduleVO = new ScheduleVO();
            scheduleVO.setPkSchedule(planViewVO.getPkPlan());
            scheduleVO.setBillstatus(planViewVO.getBillstatus() == 1 ? BillStatusConst.COMMON_APPROVED_STATUS : planViewVO.getBillstatus());
            scheduleVO.setLastFlag(planViewVO.getLastFlag());
            scheduleVO.setVersion(planViewVO.getVersion());
            scheduleVO.setSeq(planViewVO.getSeq());
            scheduleVO.setCreationtime(planViewVO.getCreationtime());
            scheduleVO.setCreator(planViewVO.getCreator());
            scheduleVO.setBillType(BillTypeConst.PROCESS_PLAN_BILLTYPE);
            scheduleVO.setPkTenant(planViewVO.getPkTenant());
            scheduleVO.setPkOriginal(planViewVO.getPkSrc());
            scheduleVO.setChildCount(planViewVO.getChildCount());
            scheduleVO.setCode(planViewVO.getScode());
            scheduleVO.setName(planViewVO.getSname());
            scheduleVO.setModifier(planViewVO.getModifier());
            scheduleVO.setModifytime(planViewVO.getModifytime());
            scheduleVO.setPlanDays(planViewVO.getExpeDuration());
            scheduleVO.setPlanStartDate(planViewVO.getExecuteStartDate());
            scheduleVO.setPlanEndDate(planViewVO.getExecuteEndDate());
            scheduleVO.setPkProject(planViewVO.getPkProject());
            scheduleVO.setChangeFlag(planViewVO.getChangeFlag());
            scheduleVO.setDr(planViewVO.getDr());

            insertScheduleList.add(scheduleVO);

            ProjectPlanViewVO[] rootPlanArray = planViewVO.getChildren();

            if(rootPlanArray == null || rootPlanArray.length <= 0){
                continue;
            }


            for(ProjectPlanViewVO rootViewVO : rootPlanArray){
                ScheduleItemVO rootItemVO = getItemVOByPlan(rootViewVO, null, rootViewVO.getPkPlan(), originVersionNameWithPkMap);

                insertItemVOList.add(rootItemVO);

                ProjectPlanViewVO[] childPlanArray = rootViewVO.getChildren();
                getInsertItemVOList(rootItemVO, childPlanArray, insertItemVOList, originVersionNameWithPkMap);

            }

        }

        String returnResult = "该租户下计划共" + planViewVOS.length + "条，根计划共" + result.size() + "条，";

        if(insertScheduleList.size() > 0){
            returnResult += "成功迁移计划共" + insertScheduleList.size() + "条，";
            MultiInsertBpTemplate<ScheduleVO> insertBpTemplate = new MultiInsertBpTemplate<ScheduleVO>(scheduleMapper, false);
            insertBpTemplate.insert(insertScheduleList.toArray(new ScheduleVO[]{}));
        }

        if(insertItemVOList.size() > 0){
            returnResult += "成功迁移任务项" + insertItemVOList.size() + "条！";
            MultiInsertBpTemplate<ScheduleItemVO> insertBpTemplate = new MultiInsertBpTemplate<ScheduleItemVO>(scheduleItemMapper, false);
            insertBpTemplate.insert(insertItemVOList.toArray(new ScheduleItemVO[]{}));
        }

        return returnResult;
    }

    private void setOriginVersionPk(Map<String, String> originVersionNameWithPkMap, ProjectPlanViewVO projectPlanViewVO){
        if(projectPlanViewVO.getVersion() == 1){
            originVersionNameWithPkMap.put(projectPlanViewVO.getSname(), projectPlanViewVO.getPkPlan());
        }

        ProjectPlanViewVO[] childViewVOS = projectPlanViewVO.getChildren();
        if(childViewVOS != null && childViewVOS.length > 0){
            for(ProjectPlanViewVO childViewVO : childViewVOS){
                setOriginVersionPk(originVersionNameWithPkMap, childViewVO);
            }
        }
    }

    private ScheduleItemVO getItemVOByPlan(ProjectPlanViewVO projectPlanViewVO, String pkParent, String pkRoot, Map<String, String> originVersionNameWithPkMap){
        ScheduleItemVO itemVO = new ScheduleItemVO();
        itemVO.setPkProject(projectPlanViewVO.getPkProject());
        itemVO.setPkScheduleItem(projectPlanViewVO.getPkPlan());
        itemVO.setPkSchedule(projectPlanViewVO.getPkRoot());
        itemVO.setPlanStartDate(projectPlanViewVO.getExecuteStartDate());
        itemVO.setPlanEndDate(projectPlanViewVO.getExecuteEndDate());
        itemVO.setPlanDays(projectPlanViewVO.getExpeDuration());
        itemVO.setChildCount(projectPlanViewVO.getChildCount());
        itemVO.setDependPlan(projectPlanViewVO.getDependPlan());
        itemVO.setName(projectPlanViewVO.getSname());
        itemVO.setCode(projectPlanViewVO.getScode());
        itemVO.setPlanFinisher(projectPlanViewVO.getPlanFinisher());
        itemVO.setIndexNumber(projectPlanViewVO.getIndexNumber());
        itemVO.setRelatedBillType(projectPlanViewVO.getRelatedBillType());
        itemVO.setRelatedPkBill(projectPlanViewVO.getRelatedPkBill());
        itemVO.setPkParent(pkParent);
        itemVO.setPkRoot(pkRoot);
        String pkOriginal = originVersionNameWithPkMap.get(projectPlanViewVO.getSname());
        itemVO.setPkOriginal(pkOriginal);
        itemVO.setVersion(projectPlanViewVO.getVersion());
        itemVO.setCompletedStatus(projectPlanViewVO.getCompletedStatus());
        itemVO.setCreationtime(projectPlanViewVO.getCreationtime());
        itemVO.setCreator(projectPlanViewVO.getCreator());
        itemVO.setModifier(projectPlanViewVO.getModifier());
        itemVO.setModifytime(projectPlanViewVO.getModifytime());
        itemVO.setDescription(projectPlanViewVO.getDescription());
        itemVO.setDr(projectPlanViewVO.getDr());
        itemVO.setChangeFlag(projectPlanViewVO.getChangeFlag());
        itemVO.setSeq(projectPlanViewVO.getSeq());
        itemVO.setPkMilestone(projectPlanViewVO.getPkMilestone());
        itemVO.setPkDepartment(projectPlanViewVO.getPkDepartment());
        itemVO.setManager(projectPlanViewVO.getManager());
        return itemVO;
    }

    private void getInsertItemVOList(ScheduleItemVO parentItemVO, ProjectPlanViewVO[] childPlanArray,
                                     List<ScheduleItemVO> insertItemVOList, Map<String, String> originVersionNameWithPkMap){
        if(childPlanArray == null || childPlanArray.length <= 0){
            return;
        }

        for(ProjectPlanViewVO viewVO : childPlanArray){
            ScheduleItemVO childItemVO = getItemVOByPlan(viewVO, parentItemVO.getPkScheduleItem(), parentItemVO.getPkRoot(), originVersionNameWithPkMap);
            insertItemVOList.add(childItemVO);
            getInsertItemVOList(childItemVO, viewVO.getChildren(), insertItemVOList, originVersionNameWithPkMap);
        }
    }

    public Map<String, List<ProjectPlanViewVO>> genChildMap(ProjectPlanViewVO[] allVOs) {
        // 构造树
        Map<String, List<ProjectPlanViewVO>> childMap = new HashMap<>();
        for (ProjectPlanViewVO vo : allVOs) {
            vo.setKey(vo.getPkPlan());
            String parent = vo.getPkParent();
            if (StringUtils.isEmpty(parent)) {
                parent = StringUtils.EMPTY;
            }
            List<ProjectPlanViewVO> children = childMap.get(parent);
            if (children == null) {
                children = new ArrayList<>();
                childMap.put(parent, children);
            }
            children.add(vo);
        }

        for (ProjectPlanViewVO vo : allVOs) {
            List<ProjectPlanViewVO> children = childMap.get(vo.getPkPlan());
            if (children != null) {
                vo.setChildren(children.toArray(new ProjectPlanViewVO[0]));
            }
        }
        return childMap;
    }
}

